package com.wvmi.gmall.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.aggregations.NestedAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsAggregate;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.ChildScoreMode;
import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.json.JsonData;
import com.fasterxml.jackson.core.type.TypeReference;
import com.wvmi.gmall.common.to.SkuEsModel;
import com.wvmi.gmall.common.utils.R;
import com.wvmi.gmall.search.constant.EsConstant;
import com.wvmi.gmall.search.feign.ProductFeignService;
import com.wvmi.gmall.search.service.MallSearchService;
import com.wvmi.gmall.search.vo.AttrResponseVo;
import com.wvmi.gmall.search.vo.SearchParam;
import com.wvmi.gmall.search.vo.SearchResult;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 描述:   <br>
 *
 * @author Wuminming <br>
 * 创建时间: 2023-11-28 9:41 <br>
 * <p>
 * Copyright: 2013 - 2023 www.szwcyq.com Inc. All rights reserved. <br>
 * 注意：本内容仅限于深圳市微创云启科技有限公司内部传阅，禁止外泄以及用于其他的商业目的<br>
 */
@Service
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    ElasticsearchClient esClient;
    @Autowired
    ProductFeignService productFeignService;

    @Override
    public SearchResult search(SearchParam param) {
        SearchRequest searchRequest = buildSearchRequest(param);
        SearchResponse<SkuEsModel> searchResponse = null;
        try {
            searchResponse = esClient.search(searchRequest, SkuEsModel.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return buildSearchResponse(searchResponse,param);
    }

    private SearchRequest buildSearchRequest(SearchParam param) {
        SearchRequest.Builder builder = new SearchRequest.Builder();
        BoolQuery.Builder boolQueryBuilder = new BoolQuery.Builder();
        // 关键字查询
        if(StringUtils.isNotBlank(param.getKeyword())){
            boolQueryBuilder.must(b->b.match(m->m.field("skuTitle").query(param.getKeyword())));
        }
        // 分类查询
        if (param.getCatalog3Id() != null) {
            boolQueryBuilder.filter(f->f.term(t->t.field("catalogId").value(param.getCatalog3Id())));
        }
        // 品牌id
        if (!CollectionUtils.isEmpty(param.getBrandId())) {
            List<FieldValue> values = param.getBrandId().stream().map(FieldValue::of).collect(Collectors.toList());
            boolQueryBuilder.filter(QueryBuilders.terms(t->t.terms(te->te.value(values))));
        }
        // 按照库存
        if(param.getHasStock() != null){
            boolQueryBuilder.filter(f->f.term(t->t.field("hasStock").value(param.getHasStock() == 1)));
        }
        // 按照价格区间
        if(StringUtils.isNotBlank(param.getSkuPrice())){
            String[] split = param.getSkuPrice().split("_");
            if (split.length == 2) {
                if(param.getSkuPrice().startsWith("_")){
                    boolQueryBuilder.filter(f->f.range(r->r.field("skuPrice").lte(JsonData.fromJson(split[1]))));
                } else {
                    boolQueryBuilder.filter(f->f.range(r->r.field("skuPrice").gte(JsonData.fromJson(split[0])).lte(JsonData.fromJson(split[1]))));
                }
            } else if(split.length == 1){
                if(param.getSkuPrice().startsWith("_")){
                    boolQueryBuilder.filter(f->f.range(r->r.field("skuPrice").lte(JsonData.fromJson(split[0]))));
                }
                if(param.getSkuPrice().endsWith("_")){
                    boolQueryBuilder.filter(f->f.range(r->r.field("skuPrice").gte(JsonData.fromJson(split[0]))));
                }
            }
        }

        // 按照属性
        if(!CollectionUtils.isEmpty(param.getAttrs())){
            for (String attrStr : param.getAttrs()) {
                BoolQuery.Builder bool = QueryBuilders.bool();
                String[] s = attrStr.split("_");
                String attrId = s[0];
                String[] attrValues = s[1].split(":");
                bool.must(m->m.term(t->t.field("attrs.attrId").value(attrId)));
                bool.must(m->m.terms(t->t.field("attrs.attrValue").terms(te->te.value(Arrays.stream(attrValues).map(FieldValue::of).collect(Collectors.toList())))));
                boolQueryBuilder.filter(f->f.nested(n->n.path("attrs").query(q->q.bool(bool.build())).scoreMode(ChildScoreMode.None)));
            }
        }
        builder.query(q-> q.bool(boolQueryBuilder.build()));

        /*
         * 排序，分页，高亮
         */
        // 排序
        if (StringUtils.isNotBlank(param.getSort())){
            String[] split = param.getSort().split("_");
            builder.sort(s->s.field(f->f.field(split[0]).order("asc".equalsIgnoreCase(split[1]) ? SortOrder.Asc : SortOrder.Desc)));
        }
        // 分页
        builder.from((param.getPageNum() - 1) * EsConstant.PRODUCT_PAGESIZE);
        builder.size(EsConstant.PRODUCT_PAGESIZE);
        // 高亮
        if(StringUtils.isNotBlank(param.getKeyword())){
            builder.highlight(h->h.fields("skuTitle",hl->hl.preTags("<b style='color:red'>").postTags("</b>")));
        }

        /*
        聚合
         */
        // 品牌的聚合
        builder.aggregations("brand_agg",agg->agg
                .terms(ta->ta.field("brandId").size(50))
                .aggregations("brand_name_agg",bag->bag.terms(tbag->tbag.field("brandName").size(1)))
                .aggregations("brand_img_agg",big->big.terms(tbig->tbig.field("brandImg").size(1))));

        // 分类的聚合
        builder.aggregations("catalog_agg",agg->agg
                .terms(t->t.field("catalogId").size(50))
                .aggregations("catalog_name_agg",cag->cag.terms(tcag->tcag.field("catalogName").size(1))));
        // 属性的聚合
        builder.aggregations("attrs_agg",a->a.nested(na->na.path("attrs"))
                .aggregations("attrId_agg",agg->agg
                        .terms(at->at.field("attrs.attrId").size(50))
                        .aggregations("attr_name_agg",ana->ana.terms(tana->tana.field("attrs.attrName").size(1)))
                        .aggregations("attr_value_agg",ava->ava.terms(tava->tava.field("attrs.attrValue").size(50)))
                )
        );

        SearchRequest sr = builder.build();
        String s = sr.toString();
        System.out.println("构建的DSL："+s);
        return sr;
    }
    private SearchResult buildSearchResponse(SearchResponse<SkuEsModel> response, SearchParam param) {
        SearchResult result = new SearchResult();
        //1、返回的所有查询到的商品
        HitsMetadata<SkuEsModel> hits = response.hits();
        //遍历所有商品信息
        if (!CollectionUtils.isEmpty(hits.hits())) {
            List<SkuEsModel> esModels = hits.hits().stream().map(i->{
                SkuEsModel source = i.source();
                if(i.highlight().containsKey("skuTitle")){
                    source.setSkuTitle(i.highlight().get("skuTitle").get(0));
                }
                return source;
            }).collect(Collectors.toList());
            result.setProduct(esModels);
        }

        //2、当前商品涉及到的所有属性信息
        List<SearchResult.AttrVo> attrVos = new ArrayList<>();
        //获取属性信息的聚合
        NestedAggregate nestedAttrAgg = (NestedAggregate) response.aggregations().get("attrs_agg")._get();
        LongTermsAggregate attrsAgg = (LongTermsAggregate) nestedAttrAgg.aggregations().get("attrId_agg")._get();
        for (LongTermsBucket bucket : attrsAgg.buckets().array()) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            //1、得到属性的id
            long attrId = bucket.key();
            attrVo.setAttrId(attrId);

            //2、得到属性的名字
            StringTermsAggregate attrNameAgg = (StringTermsAggregate) bucket.aggregations().get("attr_name_agg")._get();
            StringTermsBucket attrNameBucket = attrNameAgg.buckets().array().get(0);
            String attrName = attrNameBucket.key().stringValue();
            attrVo.setAttrName(attrName);

            //3、得到属性的所有值
            StringTermsAggregate attrValueAgg = (StringTermsAggregate) bucket.aggregations().get("attr_value_agg")._get();
            List<String> attrValues = attrValueAgg.buckets().array().stream().map(i -> i.key().stringValue()).collect(Collectors.toList());
            attrVo.setAttrValue(attrValues);
            attrVos.add(attrVo);
        }
        result.setAttrs(attrVos);

        //3、当前商品涉及到的所有品牌信息
        List<SearchResult.BrandVo> brandVos = new ArrayList<>();
        //获取到品牌的聚合
        LongTermsAggregate brandIdAgg = (LongTermsAggregate) response.aggregations().get("brand_agg")._get();
        for (LongTermsBucket brandIdBucket : brandIdAgg.buckets().array()) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            //1、得到品牌的id
            long brandId = brandIdBucket.key();
            brandVo.setBrandId(brandId);
            StringTermsAggregate brandImgAgg = (StringTermsAggregate) brandIdBucket.aggregations().get("brand_img_agg")._get();
            StringTermsBucket brandImgBucket = brandImgAgg.buckets().array().get(0);
            //3、得到品牌的图片
            String brandImg = (String) brandImgBucket.key()._get();
            brandVo.setBrandImg(brandImg);
            StringTermsAggregate brandNameAgg = (StringTermsAggregate) brandIdBucket.aggregations().get("brand_name_agg")._get();
            StringTermsBucket brandNameBucket = brandNameAgg.buckets().array().get(0);
            //2、得到品牌的名字
            String brandName = (String) brandNameBucket.key()._get();
            brandVo.setBrandName(brandName);
            brandVos.add(brandVo);
        }
        result.setBrands(brandVos);

        //4、当前商品涉及到的所有分类信息
        //获取到分类的聚合
        List<SearchResult.CatalogVo> catalogVos = new ArrayList<>();
        LongTermsAggregate catalogAgg = (LongTermsAggregate) response.aggregations().get("catalog_agg")._get();
        for (LongTermsBucket bucket : catalogAgg.buckets().array()) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            //得到分类id
            long catalogId = bucket.key();
            catalogVo.setCatalogId(catalogId);
            //得到分类名
            StringTermsAggregate catalogNameAgg = (StringTermsAggregate) bucket.aggregations().get("catalog_name_agg")._get();
            StringTermsBucket catalogNameBucket = catalogNameAgg.buckets().array().get(0);
            String catalogName = catalogNameBucket.key().stringValue();
            catalogVo.setCatalogName(catalogName);
            catalogVos.add(catalogVo);
        }
        result.setCatalogs(catalogVos);

        //5、1分页信息、总记录数
        long total = hits.total().value();
        result.setTotal(total);
        //5、分页信息-页码
        result.setPageNum(param.getPageNum());
        //5、2分页信息-总页码-计算
        int totalPages = (int)total % EsConstant.PRODUCT_PAGESIZE == 0 ?
                (int)total / EsConstant.PRODUCT_PAGESIZE : ((int)total / EsConstant.PRODUCT_PAGESIZE + 1);
        result.setTotalPages(totalPages);

        List<Integer> pageNavs = new ArrayList<>();
        for (int i = 1; i <= totalPages; i++) {
            pageNavs.add(i);
        }
        result.setPageNavs(pageNavs);

        //6、构建面包屑导航
        if (param.getAttrs() != null && param.getAttrs().size() > 0) {
            List<SearchResult.NavVo> collect = param.getAttrs().stream().map(attr -> {
                //1、分析每一个attrs传过来的参数值
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                String[] s = attr.split("_");
                navVo.setNavValue(s[1]);
                R r = productFeignService.attrInfo(Long.parseLong(s[0]));
                if (r.getCode() == 0) {
                    try {
                        AttrResponseVo data = r.getData("attr", new TypeReference<AttrResponseVo>() {});
                        navVo.setNavName(data.getAttrName());
                    } catch (IOException e) {
                        navVo.setNavName(s[0]);
                    }

                } else {
                    navVo.setNavName(s[0]);
                }

                //2、取消了这个面包屑以后，我们要跳转到哪个地方，将请求的地址url里面的当前置空
                //拿到所有的查询条件，去掉当前
                String encode = null;
                try {
                    encode = URLEncoder.encode(attr,"UTF-8");
                    encode.replace("+","%20");  //浏览器对空格的编码和Java不一样，差异化处理
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                String replace = param.get_queryString().replace("&attrs=" + attr, "");
                navVo.setLink("http://search.gmall.com/list.html?" + replace);

                return navVo;
            }).collect(Collectors.toList());

            result.setNavs(collect);
        }
        return result;
    }
}
